package com.leyou.trade.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.leyou.auth.utils.UserContext;
import com.leyou.common.constants.MQConstants;
import com.leyou.common.exception.LyException;
import com.leyou.item.client.ItemClient;
import com.leyou.item.dto.SkuDTO;
import com.leyou.trade.constants.PayConstants;
import com.leyou.trade.dto.OrderDTO;
import com.leyou.trade.dto.OrderFormDTO;
import com.leyou.trade.entity.Order;
import com.leyou.trade.entity.OrderDetail;
import com.leyou.trade.entity.OrderLogistics;
import com.leyou.trade.entity.enums.OrderStatus;
import com.leyou.trade.mapper.OrderMapper;
import com.leyou.trade.service.CartService;
import com.leyou.trade.service.OrderDetailService;
import com.leyou.trade.service.OrderLogisticsService;
import com.leyou.trade.service.OrderService;
import com.leyou.trade.utils.PayHelper;
import com.leyou.user.client.UserClient;
import com.leyou.user.dto.AddressDTO;
import feign.FeignException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;

import static com.leyou.common.constants.MQConstants.ExchangeConstants.ORDER_EXCHANGE_NAME;
import static com.leyou.common.constants.MQConstants.ExchangeConstants.TRADE_GOODS_NAME;
import static com.leyou.common.constants.MQConstants.RoutingKeyConstants.TRADE_GOODS_KEY;
import static com.leyou.common.constants.MQConstants.RoutingKeyConstants.EVICT_ORDER_KEY;

@Slf4j
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    @Autowired
    private ItemClient itemClient;

    @Autowired
    private UserClient userClient;

    @Autowired
    private OrderDetailService orderDetailService;

    @Autowired
    private OrderLogisticsService orderLogisticsService;

    @Autowired
    private PayHelper payHelper;

    @Autowired
    private AmqpTemplate amqpTemplate;

    @Autowired
    private CartService cartService;

    @Override
    @Transactional
    public Long createOrder(OrderFormDTO orderFormDTO) {

        //1.写order
        Order order = new Order();
        //1.1用户id
        order.setUserId(UserContext.getUser().getId());
        //1.2 金额相关信息
        Map<Long, Integer> carts = orderFormDTO.getCarts();
        if (CollectionUtils.isEmpty(carts) || orderFormDTO.getAddressId() == null || orderFormDTO.getPaymentType() == null) {
            throw new LyException(400, "用户输入信息有误");
        }
        //1.2.1 获取所有sku的id
        List<Long> idList = new ArrayList<>(carts.keySet());
        //1.2.2 查询sku
        List<SkuDTO> skuDTOS = itemClient.querySkuByIds(idList);
        //1.2.3 计算总金额
        long total = 0;
        for (SkuDTO skuDTO : skuDTOS) {
            // 商品数量
            Integer num = carts.get(skuDTO.getId());
            // 计算总金额
            total += num * skuDTO.getPrice();
        }
        // 1.2.4 填写金额数据
        order.setTotalFee(total);
        order.setPaymentType(orderFormDTO.getPaymentType());
        // 全场包邮
        order.setPostFee(0L);
        order.setActualFee(total + order.getPostFee());

        // 1.3 订单状态初始化
        //order.setStatus(OrderStatus.INIT);
        order.setStatus(1);
        // 1.4 写order到数据库   保存订单并主键回显
        boolean success = save(order);
        if(!success){
            throw new LyException(500, "订单创建失败！");
        }

        // 2.写OrderDetail
        // 2.1.定义一个OrderDetail的集合
        List<OrderDetail> orderDetails = new ArrayList<>();
        // 2.2.遍历sku集合，转为detail
        for (SkuDTO skuDTO : skuDTOS) {
            //2.2.1 商品数量
            Integer num = carts.get(skuDTO.getId());
            // 2.2.2.组装OrderDetail
            OrderDetail orderDetail = new OrderDetail();
            orderDetail.setOrderId(order.getOrderId());

            //substringBefore 从分隔符第一次出现的位置向前截取
            //substringBeforeLast 从分隔符最后一次出现的位置向前截取：
            //substringAfter 从分隔符第一次出现的位置向后截取
            //substringAfterLast 从分隔符最后一次出现的位置向后截取
            orderDetail.setImage(StringUtils.substringBefore(skuDTO.getImages(), ","));
            orderDetail.setNum(num);
            orderDetail.setSkuId(skuDTO.getId());
            orderDetail.setSpec(skuDTO.getSpecialSpec());
            orderDetail.setPrice(skuDTO.getPrice());
            orderDetail.setTitle(skuDTO.getTitle());
            orderDetails.add(orderDetail);
        }
        // 2.3.批量新增
        success = orderDetailService.saveBatch(orderDetails);
        if(!success){
            throw new LyException(500, "订单创建失败！");
        }

        // 3.写orderLogistics
        // 3.1.查询收货地址
        AddressDTO addressDTO = userClient.queryAddressById(orderFormDTO.getAddressId());
        // 3.2.填写物流信息
        OrderLogistics orderLogistics = addressDTO.toEntity(OrderLogistics.class);
        //主键id设置  不设置INPUT  会报错
        orderLogistics.setOrderId(order.getOrderId());
        // 3.3.写入数据库
        success = orderLogisticsService.save(orderLogistics);
        if(!success){
            throw new LyException(500, "订单创建失败！");
        }

        // 4.减库存
        try {
            itemClient.deductStock(carts);
        } catch (FeignException e) {
            throw new LyException(e.status(), e.contentUTF8());
        }

        // TODO 下单完成，还要从购物车中把已经下单的商品移除。(消息队列异步删除 在mq  jwt 用户信息失效)
//        Map<String,Object> deleteMap = new HashMap<>();
//        deleteMap.put("skuIds",idList);
//        deleteMap.put("userId",UserContext.getUser().getId());
//        amqpTemplate.convertAndSend(TRADE_GOODS_NAME,TRADE_GOODS_KEY,deleteMap);
        for (Long skuId : idList) {
            cartService.deleteCart(skuId);
        }
        // TODO 下单完成，死信队列  为付款的订单关闭并且库存回滚
        amqpTemplate.convertAndSend(ORDER_EXCHANGE_NAME, EVICT_ORDER_KEY, order.getOrderId());
        // 5.返回订单编号
        return order.getOrderId();
    }

    @Override
    public OrderDTO getByOrderId(Long orderId) {

        Order order = this.getById(orderId);
        return new OrderDTO(order);
    }

    @Override
    public String getPayUrl(Long orderId) {
        // 根据id查询订单
        Order order = this.getById(orderId);
        // 判断是否存在
        if (order == null){
            throw new LyException(400, "订单编号错误，订单不存在！");
        }
        // 判断订单状态是否是未付款
        if (order.getStatus() != 1){
            // 订单已经关闭或者已经支付，无需再次获取支付链接
            throw new LyException(400, "订单已经支付或者关闭！");
        }

        // TODO 尝试读取redis中的支付url

        // 获取订单金额
        Long actualFee = order.getActualFee();
        // 统一下单，获取支付链接
        String url = payHelper.getPayUrl(orderId, 1L/*actualFee*/, PayConstants.ORDER_DESC);

        // TODO 把支付的url缓存在redis中，2小时有效期

        return url;
    }


    /**
     * - 业务标示判断
     * - 签名校验
     * - 数据校验
     *   - 订单号码校验
     *   - 订单金额校验
     * - 更新订单状态（需要保证幂等）
     * @param data 通知内容
     */
    @Override
    @Transactional
    public void handleNotify(Map<String, String> data) {

        // 1.业务标示校验
        payHelper.checkResultCode(data);
        // 2.签名校验
        payHelper.checkResponseSignature(data);
        payHelper.checkReturnCode(data);
        // 3.订单状态校验（保证幂等，防止重复通知）
        String outTradeNo = data.get(PayConstants.ORDER_NO_KEY);
        String totalFee = data.get(PayConstants.TOTAL_FEE_KEY);
        if (StringUtils.isBlank(outTradeNo) || StringUtils.isBlank(totalFee)){
            // 数据有误
            throw new RuntimeException("响应数据有误，订单金额或编号为空！");
        }
        Long orderId = Long.valueOf(outTradeNo);
        Order order = this.getById(orderId);
        if (!order.getStatus().equals(1)){
            // 说明订单已经支付过了，属于重复通知，直接返回
            return;
        }
        // 4.订单金额校验
        Long total = Long.valueOf(totalFee);
        if (!total.equals(1L/*order.getActualFee()*/)) {
            throw new RuntimeException("订单金额有误，我要报警了！");
        }

        // 5.修改订单状态，更新状态和支付时间两个字段
        //加入乐观锁校验 update tb_order set status = 2 where order_id = #{orderId} and status = #{status}
        order.setStatus(2);
        order.setPayTime(new Date());
        this.update(order,
                new QueryWrapper<Order>()
                        .eq("status",1)   ////加入乐观锁校验
                        .eq("order_id",order.getOrderId()));
    }

    @Override
    @Transactional
    public void evictOrderIfNecessary(Long orderId) {
        // 1.查询订单
        Order order = this.getById(orderId);
        if (order == null){
            // 订单不存在，无需处理
            return;
        }
        // 2.判断订单是否支付
        if(order.getStatus() != 1){
            // 订单已处理，无需重复处理
            return;
        }

        // 3.如果未支付，需要关闭订单
        //加入乐观锁校验 update tb_order set status = 5 where order_id = #{orderId} and status = #{status}
        order.setStatus(5);
        order.setCloseTime(new Date());
        boolean update = this.update(order,
                new QueryWrapper<Order>()
                        .eq("order_id", orderId)
                        .eq("status", 1) // 通过乐观锁进一步保证幂等效果
        );
        if(!update){
            // 更新失败，订单状态已经改变，无需处理
            return;
        }
        log.info("已关闭超时未支付订单：{}", orderId);

        // 4.查询OrderDetail
        List<OrderDetail> orderDetails = orderDetailService.query().eq("order_id", orderId).list();
        // 5.获取商品及商品数量信息
        Map<Long, Integer> skuMap = new HashMap<>();
        // 得到其中的商品和数量信息
        for (OrderDetail detail : orderDetails) {
            skuMap.put(detail.getSkuId(), detail.getNum());
        }
         /*Map<Long, Integer> map = details.stream()
        .collect(Collectors.toMap(OrderDetail::getSkuId, OrderDetail::getNum));*/

        // 6.恢复库存
        itemClient.addStock(skuMap);
    }
}
